package org.finegold.core.pay.unionpay.api;

import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.codec.digest.DigestUtils;
import org.finegold.core.http.util.HttpUtil;
import org.finegold.core.pay.api.PayApi;
import org.finegold.core.pay.unionpay.enums.TradeType;
import org.finegold.core.pay.unionpay.model.UnionModel;
import org.finegold.core.tool.jackson.JsonUtil;
import org.finegold.core.tool.utils.DateUtil;
import org.finegold.core.tool.utils.Func;
import org.finegold.core.pay.unionpay.config.AppPayConfig;
import org.finegold.core.pay.unionpay.config.BasePayConfig;
import org.finegold.core.pay.unionpay.config.H5PayConfig;
import org.finegold.core.pay.unionpay.config.QrPayConfig;


import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.*;

@Slf4j
public class UnionPayApi implements PayApi {

	private final BasePayConfig basePayConfig;
	private final AppPayConfig appPayConfig;
	private final H5PayConfig h5PayConfig;
	private final QrPayConfig qrPayConfig;


	public UnionPayApi(BasePayConfig basePayConfig, AppPayConfig appPayConfig, H5PayConfig h5PayConfig, QrPayConfig qrPayConfig) {
		this.basePayConfig = basePayConfig;
		this.appPayConfig = appPayConfig;
		this.h5PayConfig = h5PayConfig;
		this.qrPayConfig = qrPayConfig;
	}

	@Override
	public Map<String, Map<String, String>> appBuild(String billNo, String totalAmount, String notifyUrl){
		UnionModel orderModel = UnionModel.builder().build();
		orderModel.setAppId(basePayConfig.getAppId());
		orderModel.setMid(this.appPayConfig.getMid());
		orderModel.setTid("1".equals(this.appPayConfig.getTid())?"00000001": this.appPayConfig.getTid());
		orderModel.setAppKey(basePayConfig.getAppKey());
		orderModel.setRequestTimestamp(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
		orderModel.setLybh(basePayConfig.getLybh());
		orderModel.setBillNo(billNo);
		orderModel.setInstMid(TradeType.ALIAPPDEFAULT.getTradeType());
		if(Func.isNotEmpty(notifyUrl)) {
			orderModel.setNotifyUrl(this.appPayConfig.getNotifyUrl() + notifyUrl);
		}
		if("0".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.ALIAPPDEFAULT.getUrl());
			orderModel.setCloseUrl(TradeType.CLOSE.getUrl());
		}else if("1".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.ALIAPPDEFAULT.getTestUrl());
			orderModel.setCloseUrl(TradeType.CLOSE.getTestUrl());
		}
		if(Func.isNotEmpty(totalAmount)) {
			orderModel.setTotalAmount(totalAmount);
		}
		String format = LocalDateTime.now().plusHours(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
		orderModel.setExpireTime(format);
		Map<String, Map<String, String>> map = UnionModel.builder().build().createSign(orderModel);
		return map;
	}
	@Override
	public Map<String, Map<String, String>> h5Build(String billNo, String totalAmount, String notifyUrl){
		UnionModel orderModel = UnionModel.builder().build();
		orderModel.setAppId(basePayConfig.getAppId());
		orderModel.setMid(h5PayConfig.getMid());
		orderModel.setTid(h5PayConfig.getTid());
		orderModel.setAppKey(basePayConfig.getAppKey());
		orderModel.setRequestTimestamp(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
		orderModel.setLybh(basePayConfig.getLybh());
		orderModel.setBillNo(billNo);
		orderModel.setInstMid(TradeType.ALIH5DEFAULT.getTradeType());
		if(Func.isNotEmpty(notifyUrl)) {
			orderModel.setNotifyUrl(h5PayConfig.getNotifyUrl() + notifyUrl);
		}
		if("0".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.ALIH5DEFAULT.getUrl());
			orderModel.setCloseUrl(TradeType.CLOSE.getUrl());
		}else if("1".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.ALIH5DEFAULT.getTestUrl());
			orderModel.setCloseUrl(TradeType.CLOSE.getTestUrl());
		}
		if(Func.isNotEmpty(totalAmount)) {
			orderModel.setTotalAmount(totalAmount);
		}
		String format = LocalDateTime.now().plusHours(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
		orderModel.setExpireTime(format);
		Map<String, Map<String, String>> map = UnionModel.builder().build().createSign(orderModel);
		return map;
	}
	@Override
	public Map<String, Map<String, String>> qrBuild(String billNo, String totalAmount, String notifyUrl,String qrCodeId){
		UnionModel orderModel = UnionModel.builder().build();
		orderModel.setAppId(basePayConfig.getAppId());
		orderModel.setMid(qrPayConfig.getMid());
		orderModel.setTid(qrPayConfig.getTid());
		orderModel.setAppKey(basePayConfig.getAppKey());
		orderModel.setRequestTimestamp(DateUtil.format(new Date(),"yyyy-MM-dd HH:mm:ss"));
		orderModel.setLybh(basePayConfig.getLybh());
		orderModel.setBillNo(billNo);
		orderModel.setInstMid(TradeType.QRPAYDEFAULT.getTradeType());
		if(Func.isNotEmpty(notifyUrl)) {
			orderModel.setNotifyUrl(qrPayConfig.getNotifyUrl() + notifyUrl);
		}
		if(Func.isNotEmpty(qrCodeId)){
			orderModel.setQrCodeId(qrCodeId);
		}
		if("0".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.QRPAYDEFAULT.getUrl());
			orderModel.setCloseUrl(TradeType.CLOSE_QRCODE.getUrl());
		}else if("1".equals(basePayConfig.getIsTest())){
			orderModel.setSeverUrl(TradeType.QRPAYDEFAULT.getTestUrl());
			orderModel.setCloseUrl(TradeType.CLOSE_QRCODE.getTestUrl());
		}
		if(Func.isNotEmpty(totalAmount)) {
			orderModel.setTotalAmount(totalAmount);
		}
		String format = LocalDateTime.now().plusHours(2).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
		orderModel.setExpireTime(format);
		Map<String, Map<String, String>> map = UnionModel.builder().build().createSign(orderModel);
		return map;
	}

	/**
	 * <B>Description:</B> 获取返回参数
	 * @author QinXueFeng
	 * @date 2020/9/8 12:42
	 * @param valueType
	 * @return
	 */
	@Override
	public  <T> T  send(Map<String, Map<String, String>> map, Class<T> valueType) {
		log.info("发送报文======================"+map);
		String json1 = HttpUtil.postJson(map.get("body").get("severUrl"), map.get("head"), new JSONObject((Map) map.get("json")).toString());
		T parse = JsonUtil.parse(json1, valueType);
		return parse;
	}

	/**
	 * <B>Description:</B> 获取返回参数
	 * @author QinXueFeng
	 * @date 2020/9/8 12:42
	 * @param map
	 * @return
	 */
	@Override
	public  Map  close(Map<String, Map<String, String>> map) {
		log.info("发送报文======================"+map);
		String json1 = HttpUtil.postJson(map.get("body").get("closeUrl"), map.get("head"), new JSONObject((Map) map.get("json")).toString());
		Map parse = JsonUtil.parse(json1, Map.class);
		return parse;
	}

	// 获取HttpServletRequest里面的参数
	public Map<String, String> getRequestParams(HttpServletRequest request) {
		Map<String, String[]> params = request.getParameterMap();
		Map<String, String> params2 = new HashMap<>();
		for (String key : params.keySet()) {
			String[] values = params.get(key);
			if (values.length > 0) {
				params2.put(key, request.getParameter(key));
			}
		}
		return params2;
	}


	public Boolean makeSign(Map<String, String> params) {
		String preStr = buildSignString(params); // 把数组所有元素，按照“参数=参数值”的模式用“&”字符拼接成字符串
		String text = preStr + basePayConfig.getMd5Key();
		log.info("待签名字符串：" + text);
		return DigestUtils.md5Hex(getContentBytes(text)).toUpperCase().equals(params.get("sign"));
	}

	// 构建签名字符串
	private String buildSignString(Map<String, String> params) {

		// params.put("Zm","test_test");

		if (params == null || params.size() == 0) {
			return "";
		}

		List<String> keys = new ArrayList<>(params.size());

		for (String key : params.keySet()) {
			if ("sign".equals(key))
				continue;
			if (Func.isEmpty(params.get(key)))
				continue;
			keys.add(key);
		}
		//System.out.println(listToString(keys));

		Collections.sort(keys);

		StringBuilder buf = new StringBuilder();

		for (int i = 0; i < keys.size(); i++) {
			String key = keys.get(i);
			String value = params.get(key);

			if (i == keys.size() - 1) {// 拼接时，不包括最后一个&字符
				buf.append(key + "=" + value);
			} else {
				buf.append(key + "=" + value + "&");
			}
		}

		return buf.toString();
	}

	// 根据编码类型获得签名内容byte[]
	private static byte[] getContentBytes(String content) {
		try {
			return content.getBytes("UTF-8");
		} catch (UnsupportedEncodingException e) {
			throw new RuntimeException("签名过程中出现错误");
		}
	}
	public String getLybh(){
		return basePayConfig.getLybh();
	}
}
